package com.ning.city.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ning.city.bean.Token;
import com.ning.city.bean.User;
import com.ning.city.dto.UserDto;
import com.ning.city.mapper.TokenMapper;
import com.ning.city.mapper.UserMapper;
import com.ning.city.vo.CommonResult;
import com.ning.city.vo.PageData;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

/**
 * @author Ning 242741154@qq.com
 * @date 2021/1/5 9:52
 */
@Service
public class UserService {
    private final UserMapper userMapper;
    private final TokenMapper tokenMapper;
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    public UserService(UserMapper userMapper, TokenMapper tokenMapper, RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper) {
        this.userMapper = userMapper;
        this.tokenMapper = tokenMapper;
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
    }

    private static final Duration ORDINARY_EXPIRY_DURATION
            = Duration.ofDays(1);
    private static final Duration ADMIN_EXPIRY_DURATION
            = Duration.ofHours(2);
    public static final String USER_REDIS_PREFIX = "user-";

    public Integer getCount() {
        QueryWrapper<User> query = new QueryWrapper<>();
        query.ne("state", 4);
        return userMapper.selectCount(query);
    }

    public PageData<User> getPageData(UserDto.PageInfo pageInfo) {
        PageData<User> pageData = new PageData<>();
        pageData.setPageInfo(pageInfo);
        pageData.setCount(getCount());
        QueryWrapper<User> query = new QueryWrapper<>();
        // 根据类型查找
        Integer userType = pageInfo.getUserType();
        // 不选择密码
        query.select("id", "name", "type");
        query.eq(userType != null && userType != 0,
                "type", userType);
//        // 根据关键字查找
//        String keyword = pageInfo.getKeyword();
//        query.like(keyword != null && !keyword.equals(""),
//                "name", keyword);
        // 分页信息
        int pageNum = pageInfo.getPageNum();
        int pageSize = pageInfo.getPageSize();
        query.last("limit "
                + (pageNum - 1) * pageSize + "," + pageSize);
        // 执行查询
        List<User> infos = userMapper.selectList(query);
//        infos.forEach(e -> {
//            e.setPassword(null);
//        });
        pageData.setData(infos);
        return pageData;
    }

    /**
     * 根据不同的用户权限选择过期时间
     *
     * @param type 用户类型(权限)
     * @return Duration
     */
    private Duration getDurationByUserType(Short type) {
        if (type == null || type == 3) {
            return ORDINARY_EXPIRY_DURATION;
        }
        return ADMIN_EXPIRY_DURATION;
    }

    /**
     * 注册
     *
     * @param dto 注册 dto
     * @return 新注册用户的信息
     */
    public User register(UserDto.Register dto) {
        final User user = new User();
        user.setName(dto.getName());
        user.setPassword(DigestUtils.md5DigestAsHex(
                dto.getPassword().getBytes()
        ));
        if (userMapper.insert(user) == 1) {
            System.out.println(user.getId());
        }
        User user1 = new User();
        user1.setId(user.getId());
        return user;
    }

    /**
     * 登录
     * <p>
     * 采用类似 QQ 的策略--新登录挤掉旧登录,
     * 即不在乎用户是否已在其它地方登录,
     * 只要信息正确便派发 token
     * </p>
     *
     * @param dto UserDto.Login
     * @return err-msg or tokenString(begin with '&')
     */
    public CommonResult<Token> login(UserDto.Login dto) {
        final CommonResult<Token> result = new CommonResult<>();
        final User user = userMapper.selectById(dto.getId());
        if (user == null) {
            result.setCode(400);
            result.setErrors(Collections.singletonList("账号有误!"));
            return result;
        }
        // 检查密码
        boolean bool = user.getPassword().equals(DigestUtils
                .md5DigestAsHex(dto.getPassword().getBytes()));
        if (!bool) {
            result.setCode(400);
            result.setErrors(Collections.singletonList("密码有误!"));
            return result;
        } else {
            // 生成 token
            Token token = new Token(UUID.randomUUID()
                    .toString().replace("-", ""),
                    user.getId(), user.getType(), LocalDateTime.now());
            // 插入到数据库中
            tokenMapper.insert(token);
            try {
                ValueOperations<String, String> ops
                        = redisTemplate.opsForValue();
                String tokenString = objectMapper.writeValueAsString(token);
                Short type = user.getType();
                Duration duration = getDurationByUserType(type);
                ops.set(USER_REDIS_PREFIX + user.getId(), tokenString, duration);
                result.setData(token);
                return result;
            } catch (JsonProcessingException e) {
                result.setCode(400);
                result.setErrors(Collections.singletonList("其它错误!"));
                return result;
            }
        }
    }

    public boolean delete(String userId) {
        final User user = new User();
        user.setId(userId);
        user.setState((short) 4);
        return userMapper.updateById(user) == 1;
    }

    public boolean updateName(UserDto.UpdateName dto) {
        final User user = new User();
        user.setId(dto.getUserId());
        user.setName(dto.getNewName());
        return userMapper.updateById(user) == 1;
    }

    /**
     * 修改密码
     *
     * @param dto dto
     * @return 错误信息, 值为 null 时, 操作成功
     */
    public String updatePassword(UserDto.UpdatePassword dto) {
        User user = userMapper.selectById(dto.getUserId());
        if (user == null) {
            return "用户不存在!";
        }
        String oldPass = DigestUtils.md5DigestAsHex(dto.getOldPassword().getBytes());
        if (!user.getPassword().equals(oldPass)) {
            return "旧密码有误!";
        }
        String newPass = DigestUtils.md5DigestAsHex(dto.getNewPassword().getBytes());
        user.setPassword(newPass);
        if (userMapper.updateById(user) == 1) {
            return null;
        }
        return "修改失败!";
    }
}
